package com.lagou.sqlSession;

import com.lagou.config.BoundSql;
import com.lagou.pojo.Configuration;
import com.lagou.pojo.MappedStatement;
import com.lagou.utils.GenericTokenParser;
import com.lagou.utils.ParameterMapping;
import com.lagou.utils.ParameterMappingTokenHandler;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 查询接口的实现类
 */
public class SimpleExecutor implements Executor{

    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params)
            throws Exception {

        /* 1.注册驱动，获取连接 */
        Connection connection = configuration.getDataSource().getConnection();

        //2.获取sql语句 select * from user where id = #{id} and username =#{username}
        String sql = mappedStatement.getSql();

        //转换为数据库能够执行的sql语句
        BoundSql boundSql = getBoundSql(sql);
        //3.获取预处理对象
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());

        //4.设置参数
        //获取参数的全路径
        String parameterType = mappedStatement.getParamterType();
        //获取参数的类的Class对象
        Class<?> parameterTypeClass = getClassType(parameterType);
        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
        for(int i=0;i<parameterMappingList.size();i++){
            ParameterMapping parameterMapping = parameterMappingList.get(i);
            //属性名称
            String content = parameterMapping.getContent();
            //反射
            //属性的Filed对象
            Field field = parameterTypeClass.getDeclaredField(content);
            //暴力访问，防止设置为private访问不了
            field.setAccessible(true);
            //获取到对应的属性值
            Object obj = field.get(params[0]);
            //给预处理对象设置值 注意：sql预处理是从1开始设置值的
            preparedStatement.setObject(i+1,obj);
        }

        //5.执行sql
        ResultSet resultSet = preparedStatement.executeQuery();

        //6.封装结果集返回
        //返回值类型
        String resultType = mappedStatement.getResultType();
        //返回值类型的Class对象
        Class<?> resultTypeClass = getClassType(resultType);
        List<Object> objectList = new ArrayList<>();

        //查询的结果集
        while (resultSet.next()){
            Object o = resultTypeClass.newInstance();
            //元数据：每一行的数据
            ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
            //封装字段的值到字段名:遍历一行数据的每个字段，封装结果集
            for(int i=1;i<=resultSetMetaData.getColumnCount();i++){
                //字段名
                String columnName = resultSetMetaData.getColumnName(i);
                //字段值
                Object value = resultSet.getObject(columnName);
                //使用反射，根据数据库和实体的对应关系，完成封装
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName,resultTypeClass);
                Method writeMethod = propertyDescriptor.getWriteMethod();
                //通过method的invoke方法，给字段赋值
                writeMethod.invoke(o,value);
            }
            objectList.add(o);
        }
        return (List<E>) objectList;
    }


    /**
     * 根据sql参数类型的全路径获取对应的类的Class对象
     * @param parameterType sql参数类型的全路径
     * @return
     * @throws ClassNotFoundException
     */
    private Class<?> getClassType(String parameterType) throws ClassNotFoundException {
        if(parameterType!=null){
            Class<?> aClass = Class.forName(parameterType);
            return aClass;
        }else{
            return null;
        }
    }

    /**
     * 完成对#{}sql的解析工作，1.将#{}使用问号就行代替，2.解析出#{}的值就行存储
     * @param sql
     * @return
     */
    private BoundSql getBoundSql(String sql){
        //创建标记处理类：配置标记解析器来完成对占位符的解析处理工作
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        //创建标记解析器
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{","}",parameterMappingTokenHandler);

        //拿到解析过后的sql
        String parseSql = genericTokenParser.parse(sql);

        //#{}里面解析出来的参数名称
        List<ParameterMapping> parameterMappingList = parameterMappingTokenHandler.getParameterMappings();

        //存放的是解析完成的sql和他的参数列表
        BoundSql boundSql = new BoundSql(parseSql,parameterMappingList);
        return boundSql;
    }

    //新增、修改、删除执行器
    @Override
    public int update(Configuration configuration,MappedStatement ms, Object... params) throws Exception {
        //1.注册驱动，获取连接
        Connection connection = configuration.getDataSource().getConnection();

        //2.获取sql语句
        String sql = ms.getSql();
        //转换为数据库能够执行的sql语句
        BoundSql boundSql = getBoundSql(sql);

        //3.获取预处理对象
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());

        //4.设置参数：
        String parameterType = ms.getParamterType();
        //获取参数的类的Class对象
        Class<?> parameterTypeClass = getClassType(parameterType);
        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
        for(int i=0;i<parameterMappingList.size();i++){
            ParameterMapping parameterMapping = parameterMappingList.get(i);
            //属性名称
            String content = parameterMapping.getContent();
            //反射
            //属性的Filed对象
            Field field = parameterTypeClass.getDeclaredField(content);
            //暴力访问，防止设置为private访问不了
            field.setAccessible(true);
            //获取到对应的属性值
            Object obj = field.get(params[0]);
            //给预处理对象设置值 注意：sql预处理是从1开始设置值的
            preparedStatement.setObject(i+1,obj);
        }

        //5.执行更新操作
        preparedStatement.execute();

        //6.获取更新数量
        int rows = preparedStatement.getUpdateCount();
        return rows;
    }

}
